package com.uwlaser.webservice.client;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.predic8.wsdl.*;
import com.predic8.xml.util.ResourceDownloadException;
import com.uwlaser.utils.StringUtils;
import com.uwlaser.webservice.exception.WebServiceCodeErrorException;
import com.uwlaser.webservice.exception.WebServiceException;
import com.uwlaser.webservice.exception.WebServiceTimeoutException;
import org.dom4j.*;

import java.util.*;

/**
 * @Author QZK
 * @Description Wsdl解析器
 * @Date 2022-11-23-14:51
 * @Version 1.0
 */
public class WsdlResolver {

    /**
     * 解析
     * @param url
     */
    public static WsdlEntity resolve(String url) throws Exception {

        WsdlEntity wsdlEntity = null;
        //判断Wsdl模板是否存在
        if(!WsdlTemplate.existsTemplate(url)) {
            WsdlEntity wsdl = null;
            //没有就根据url解析
            try {
                wsdl = new WsdlEntity(url);
            } catch (Exception e) {
                String message = "";
                if (e instanceof ResourceDownloadException){
                    ResourceDownloadException rde = (ResourceDownloadException) e;
                    message = rde.getRootCause().getMessage();
                } else {
                    message = e.getMessage();
                }
                throw new WebServiceTimeoutException("解析XML模板时，根据URL【"+url+"】链接服务器失败！异常信息：" + message);
            }

            //解析模板
            try {
                wsdlEntity = resolveWsdl(wsdl);
            } catch (Exception e) {
                if(e instanceof WebServiceException){
                    throw e;
                } else {
                    throw new WebServiceCodeErrorException("解析XML模板时发生异未知异常！异常信息：" + e.getMessage());
                }
            }

        } else {
            //有就根据url从模板中获取
            wsdlEntity = WsdlTemplate.getTemplate(url);
        }
        return wsdlEntity;
    }

    /**
     * 解析Wsdl
     * @param wsdlEntity
     */
    public synchronized static WsdlEntity resolveWsdl(WsdlEntity wsdlEntity){
        //再次判断
        if(WsdlTemplate.existsTemplate(wsdlEntity.getUrl())) {
            return WsdlTemplate.getTemplate(wsdlEntity.getUrl());
        }
        //解析wsdl的文档  service 标签,一个文档一般只有一个
        for (Service service : wsdlEntity.getWsdl().getServices()) {
            //解析wsdl的文档  ports 标签
            for (Port port : service.getPorts()) {
                Binding binding = port.getBinding();
                List<BindingOperation> operations = binding.getOperations();
                //解析wsdl的文档  operation 标签，代表方法名
                for (BindingOperation operation : operations) {
                    //跳过解析
                    if(binding.getName().indexOf("HttpGet") != -1 || binding.getName().indexOf("HttpPost") != -1){
                        continue;
                    }
                    //方法实体
                    WsdlMethodEntity wsdlMethodEntity = new WsdlMethodEntity();
                    //方法名称
                    wsdlMethodEntity.setMethodName(operation.getName());

                    //请求模板
                    List<BindingElement> bindingElements = operation.getInput().getBindingElements();
                    wsdlMethodEntity.setBindingElements(bindingElements);

                    wsdlEntity.getCreator().createRequest(port.getName(), operation.getName(), binding.getName());
                    wsdlMethodEntity.setRequestTemplate(wsdlEntity.getWriter().toString());
                    wsdlEntity.getWriter().getBuffer().setLength(0);

                    wsdlEntity.getMethods().add(wsdlMethodEntity);
                }
            }
        }
        //保存到全局变量中
        WsdlTemplate.putTemplate(wsdlEntity.getUrl(),wsdlEntity);
        return wsdlEntity;
    }


    public static String getXmlContent(WsdlMethodEntity methodEntity,String methodName, String bodyParams, String headerParams) throws Exception {
        //获取方法的xml模板
        String xmlStr = methodEntity.getRequestTemplate();
        //把xml转为文档
        Document doc = DocumentHelper.parseText(xmlStr);
        //从根节点开始解析
        org.dom4j.Element rootElement = doc.getRootElement();
        List<org.dom4j.Element> elements = rootElement.elements();
        for (org.dom4j.Element element : elements) {
            String name = element.getName();
            if (name.equals("Header")){
                if(StringUtils.isEmpty(headerParams)){
                    throw new WebServiceCodeErrorException("调用方法名称【" + methodName + "】失败，请求头参数不能为空！");
                }
                jsonToXml(element.elements().get(0) ,headerParams);
            }
            if(name.equals("Body")){
                if(StringUtils.isEmpty(bodyParams)){
                    throw new WebServiceCodeErrorException("调用方法名称【" + methodName + "】失败，请求体参数不能为空！");
                }
                //获取方法节点
                org.dom4j.Element methodElement = resolveMethod(methodEntity.getMethodName(), element);
                if(methodElement == null){
                    throw new WebServiceCodeErrorException("调用方法名称【" + methodName + "】失败，请求模板中无法匹配到方法名称！");
                }
                jsonToXml(methodElement ,bodyParams);
            }
        }
        //得到XML节点字符串
        return "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + rootElement.asXML();
    }

    /**
     * 删除子节点
     * @param element
     * @return
     */
    private static Namespace delChildrenElement(org.dom4j.Element element){
        List<org.dom4j.Element> elements2 = element.elements();
        Namespace namespace = null;
        for (org.dom4j.Element element2 : elements2) {
            if(element2.getQualifiedName().indexOf(":") != -1 && namespace == null){
                namespace = element2.getNamespace();
            }
            element.remove(element2);
        }
        return namespace;
    }

    /**
     *  解析方法
     * @param methodName
     * @param element
     * @return
     */
    private static org.dom4j.Element resolveMethod(String methodName,org.dom4j.Element element){
        List<org.dom4j.Element> bodyElements = element.elements();
        if(bodyElements.size() == 0){
            return null;
        }
        for (org.dom4j.Element bodyElement : bodyElements) {
            String elementName = bodyElement.getName();
            if(methodName.equals(elementName)){
                return bodyElement;
            } else {
                org.dom4j.Element element1 = resolveMethod(methodName, bodyElement);
                if(element1 != null){
                    return element1;
                }
            }
        }
        return null;
    }

    /**
     * json转xml<br>
     * 方 法 名：jsonToXml <br>
     * @param json
     * @return String
     */
    public static String jsonToXml(org.dom4j.Element element1, String json) throws Exception {
        // 判断模板中是否有命名空间，如果有组装的参数也需要组装上命名空间
        Namespace namespace = delChildrenElement(element1);
        String prefix = "";
        String namespaceText = "";
        if(namespace != null){
            prefix = namespace.getPrefix() + ":";
            namespaceText = " xmlns:"+namespace.getPrefix() + "=\"" + namespace.getText() + "\"";
        }

        //字符串转JSON对象
        JSONObject jObj = JSON.parseObject(json);

        //根据JSON对象递归组装xml
        StringBuffer buffer = new StringBuffer();
        buffer.append("<"+ prefix +"root"+namespaceText+">");
        jsonToXmlstr(jObj,buffer,prefix);
        buffer.append("</"+ prefix +"root>");
        String xmlStr = buffer.toString();

        //把root节点下的节点加入请求模板中
        Element rootElement = DocumentHelper.parseText(xmlStr).getRootElement();
        List<Element> elements = rootElement.elements();
        if(elements != null && elements.size() > 0){
            for (Element element : elements) {
                //从root节点中删除该节点后，再不该节点加入请求模板中
                rootElement.remove(element);
                element1.add(element);
            }
        }
        return xmlStr;
    }
    /**
     * json转str<br>
     * 方 法 名：jsonToXmlstr <br>
     * @param jObj
     * @param buffer
     * @return String
     */
    public static String jsonToXmlstr(JSONObject jObj, StringBuffer buffer ,String prefix){

        Set<Map.Entry<String, Object>> se = jObj.entrySet();
        for(Iterator<Map.Entry<String, Object>> it = se.iterator(); it.hasNext(); ) {
            Map.Entry<String, Object> en = it.next();
            if(en.getValue() != null && en.getValue().getClass().getName().equals("com.alibaba.fastjson.JSONObject")){
                buffer.append("<" + prefix + en.getKey()+">");
                JSONObject jo = jObj.getJSONObject(en.getKey());
                jsonToXmlstr(jo,buffer,prefix);
                buffer.append("</" + prefix + en.getKey()+">").append("\r\n");
            }else if(en.getValue() != null && en.getValue().getClass().getName().equals("com.alibaba.fastjson.JSONArray")){
                JSONArray jarray = jObj.getJSONArray(en.getKey());
                for (int i = 0; i < jarray.size(); i++) {
                    buffer.append("<" + prefix + en.getKey()+">");
                    Object val = jarray.get(i);
                    if(val instanceof JSONObject){
                        JSONObject jsonobject =  jarray.getJSONObject(i);
                        jsonToXmlstr(jsonobject,buffer,prefix);
                    }else{
                        buffer.append(val);
                    }
                    buffer.append("</" + prefix + en.getKey()+">").append("\r\n");
                }
            }else if(en.getValue() != null && en.getValue().getClass().getName().equals("java.lang.String")){
                buffer.append("<" + prefix + en.getKey()+">"+en.getValue());
                buffer.append("</" + prefix + en.getKey()+">").append("\r\n");
            }else{
                if(en.getValue() != null){
                    buffer.append("<" + prefix + en.getKey()+">"+en.getValue().toString());
                    buffer.append("</" + prefix + en.getKey()+">").append("\r\n");
                } else {
                    buffer.append("<" + prefix + en.getKey()+">"+"");
                    buffer.append("</" + prefix + en.getKey()+">").append("\r\n");
                }
            }
        }
        return buffer.toString();
    }

    /**
     * 解析webservice的返回结果
     * @param xmlStr xml内容
     * @return
     */
    public static JSONObject xml2Json(String xmlStr) throws Exception {
        Document doc = DocumentHelper.parseText(xmlStr);
        JSONObject json = new JSONObject();
        dom4j2Json(doc.getRootElement(), json);
        return json;
    }


    /**
     * xml转json
     * @param element
     * @param json
     */
    public static void dom4j2Json(org.dom4j.Element element, JSONObject json) {
        List<org.dom4j.Element> chdEl = element.elements();
        for(org.dom4j.Element e : chdEl){
            if (!e.elements().isEmpty()) {
                JSONObject chdjson = new JSONObject();
                dom4j2Json(e, chdjson);
                Object o = json.get(e.getName());
                if (o != null) {
                    JSONArray jsona = null;
                    if (o instanceof JSONObject) {
                        JSONObject jsono = (JSONObject) o;
                        json.remove(e.getName());
                        jsona = new JSONArray();
                        jsona.add(jsono);
                        jsona.add(chdjson);
                    }
                    if (o instanceof JSONArray) {
                        jsona = (JSONArray) o;
                        jsona.add(chdjson);
                    }
                    json.put(e.getName(), jsona);
                } else {
                    if (!chdjson.isEmpty()) {
                        json.put(e.getName(), chdjson);
                    }
                }
            } else {
                if (!e.getText().isEmpty()) {
                    if(json.get(e.getName()) == null){
                        json.put(e.getName(), e.getText());
                    } else {
                        Object o = json.get(e.getName());
                        JSONArray l = null;
                        if (o instanceof JSONArray) {
                            l = (JSONArray) o;
                        } else {
                            l = new JSONArray();
                            l.add(o);
                        }
                        l.add(e.getText());
                        json.put(e.getName(), l);
                    }
                }
            }
        }
    }
}
